package learning.pcfg.training;

import java.util.HashMap;
import java.util.Map;

import learning.data.document.TreeDocument;
import learning.pcfg.inference.CYKParser;
import learning.pcfg.inference.IParseScorer;
import learning.pcfg.inference.Parse;
import learning.pcfg.inference.Region;
import learning.pcfg.inference.IndexedGrammar.Production;
import learning.util.SparseVector;

public class PCFG {

	public static DecodingResult decode(TreeDocument doc,
			IParseScorer parseScorer, PCFGParameters iterParameters) {
		
		parseScorer.setDocument(doc);
		parseScorer.setParameters(iterParameters);
		
		CYKParser parser = new CYKParser(iterParameters.grammar, parseScorer);
		
		Region input = new Region(0, doc.tokens.length);
		
		Parse parse = parser.parse(input);
		
		return new DecodingResult(parse);
	}
	
	public static void update(TreeDocument doc, 
			DecodingResult predictedParse, DecodingResult trueParse,  
			PCFGParameters iterParameters, float delta) {
		
		HashMap<ProductionRegion, Float> pendingUpdates = 
			new HashMap<ProductionRegion, Float>();
		
		for (Parse p : predictedParse.parse.getTreeNodes()) {
			ProductionRegion pr = new ProductionRegion(p);
			Float v = pendingUpdates.get(pr);
			if (v == null) v = new Float(0);
			pendingUpdates.put(pr, v - delta);
		}
		
		for (Parse p : trueParse.parse.getTreeNodes()) {
			ProductionRegion pr = new ProductionRegion(p);
			Float v = pendingUpdates.get(pr);
			if (v == null) v = new Float(0);
			pendingUpdates.put(pr, v + delta);
		}
		
		for (Map.Entry<ProductionRegion, Float> e : pendingUpdates.entrySet()) {
			if (Math.abs(e.getValue()) > 1e-6) {
				
				ProductionRegion pr = e.getKey();
				float pdelta = e.getValue();
				
				Production prod = iterParameters.grammar.productions[pr.parse.production];
				if (!prod.learnable) continue;
				
				SparseVector sv = iterParameters.productionParameters[pr.parse.production];
				
				// let features be the sparse vector of features for pr
				//sv.sum(features, pdelta);
				
				//for (int t = pr.parse.region.start; t < pr.parse.region.end; t++) {
				//	doc.
				//}
				
			}
		}
	}
	
	
	
	
	
	private static class ProductionRegion {
		private Parse parse;
		
		private ProductionRegion(Parse parse) {
			this.parse = parse;
		}
		
		public boolean equals(Object o) {
			if (!(o instanceof ProductionRegion)) return false;
			ProductionRegion pr = (ProductionRegion)o;
            return (parse.region.start == pr.parse.region.start &&
                    parse.region.end == pr.parse.region.end &&
                    parse.production == pr.parse.production);
		}
		
		public int hashCode() {
            return parse.region.start << 24 +
            parse.region.end << 16 +
            parse.production;
		}
	}
	
	public static class DecodingResult {
		public Parse parse;
		public int[] labels;
		
		public DecodingResult(Parse parse) {
			this.parse = parse;
		}
	}
}
